function OSGBParser(owner) {
    this.owner = owner;
    this.meshNames = [];
    this.meshDatas = [];
    this.textureDatas = [];
    this.lodRanges = [];
    this.rangeDatas = [];
}

Object.assign(OSGBParser.prototype, {

    load: function (url) {
        var that = this;
        var xhr = new XMLHttpRequest();
        xhr.timeout = 0;
        xhr.withCredentials = false;
        xhr.responseType = "arraybuffer";
        xhr.onload = function() {
            var response = pako.inflate(this.response).buffer;
            that.parser(response);
            that.owner.loaded(that);
        }
        xhr.open("GET", url, true);
        xhr.send(null);
        return true;
    },

    parser: function (dataBuffer) {
        this.meshNames.length = 0;
        this.meshDatas.length = 0;
        this.textureDatas.length = 0;
        this.lodRanges.length = 0;
        this.rangeDatas.length = 0;

        var dataView = new DataView(dataBuffer);
        var textDecoder = new TextDecoder('utf-8');
        var littleEndian = true;
        var byteOffset = 0;
        var lodMeshCount = dataView.getInt32(byteOffset, littleEndian);
        byteOffset += 4;
        for (var i = 0; i < lodMeshCount; i++) {
            var meshCount = dataView.getInt32(byteOffset, littleEndian);
            byteOffset += 4;
            for (var j = 0; j < meshCount; j++) {
                var meshData = new OSGBMeshData();

                var positionCount = dataView.getInt32(byteOffset, littleEndian);
                byteOffset += 4;
                var positions = new Float32Array(dataBuffer.slice(byteOffset, byteOffset + 4 * positionCount));
                //var positions = new Float32Array(dataBuffer, byteOffset, positionCount);
                byteOffset += 4 * positionCount;
                meshData.positions = positions;

                var texCoordCount = dataView.getInt32(byteOffset, littleEndian);
                byteOffset += 4;
                var texCoords = new Float32Array(dataBuffer.slice(byteOffset, byteOffset + 4 * texCoordCount));
                //var texCoords = new Float32Array(dataBuffer, byteOffset, texCoordCount);
                byteOffset += 4 * texCoordCount;
                meshData.texCoords = texCoords;

                var indexCount = dataView.getInt32(byteOffset, littleEndian);
                byteOffset += 4;
                var indexs = new Uint16Array(dataBuffer.slice(byteOffset, byteOffset + 2 * indexCount));
                //var indexs = new Uint16Array(dataBuffer, byteOffset, indexCount);
                byteOffset += 2 * indexCount;
                meshData.indexs = indexs;

                var boundingBoxSize = dataView.getInt32(byteOffset, littleEndian);
                byteOffset += 4;
                var boundingBox = new Float32Array(dataBuffer.slice(byteOffset, byteOffset + 4 * boundingBoxSize));
                //var boundingBox = new Float32Array(dataBuffer, byteOffset, boundingBoxSize);
                byteOffset += 4 * boundingBoxSize;
                meshData.boundingBox = boundingBox;

                var sphereBoxSize = dataView.getInt32(byteOffset, littleEndian);
                byteOffset += 4;
                var sphereBox = new Float32Array(dataBuffer.slice(byteOffset, byteOffset + 4 * sphereBoxSize));
                //var sphereBox = new Float32Array(dataBuffer, byteOffset, sphereBoxSize);
                byteOffset += 4 * sphereBoxSize;
                meshData.sphereBox = sphereBox;

                var textureData = new OSGBTextureData();
                var textureNameLength = dataView.getInt32(byteOffset, littleEndian);
                byteOffset += 4;
                if (textureNameLength > 0) {
                    textureData.name = textDecoder.decode(new Uint8Array(dataBuffer, byteOffset, textureNameLength));
                    byteOffset += textureNameLength;
                }
                var textureFormat = dataView.getInt32(byteOffset, littleEndian);
                byteOffset += 4;
                textureData.width = dataView.getInt32(byteOffset, littleEndian);
                byteOffset += 4;
                textureData.height = dataView.getInt32(byteOffset, littleEndian);
                byteOffset += 4;
                textureData.compress = dataView.getInt32(byteOffset, littleEndian);
                byteOffset += 4;
                textureData.miplevel = dataView.getInt32(byteOffset, littleEndian);
                byteOffset += 4;
                var textureBufferSize = dataView.getInt32(byteOffset, littleEndian);
                byteOffset += 4;
                var textureBuffer = null;
                if (textureBufferSize > 0) {
                    textureBuffer = dataBuffer.slice(byteOffset, byteOffset + textureBufferSize);
                    //textureBuffer = new Uint8Array(dataBuffer, byteOffset, textureBufferSize);
                    byteOffset += textureBufferSize;
                }
                if (textureData.compress == 14) {
                    if (textureFormat == 24) {
                        var mipmap = {};
                        mipmap.width = textureData.width;
                        mipmap.height = textureData.height;
                        var levelSize = Math.max(4, mipmap.width) / 4 * Math.max(4, mipmap.height) / 4 * 8;
                        mipmap.data = new Uint8Array(textureBuffer.slice(0, levelSize));
                        textureData.mipmaps.push(mipmap);
                        textureData.format = 33777;
                        textureData.type = 1009;
                    } else if (textureFormat == 32) {
                        var mipmap = {};
                        mipmap.width = textureData.width;
                        mipmap.height = textureData.height;
                        var levelSize = Math.max(4, mipmap.width) / 4 * Math.max(4, mipmap.height) / 4 * 16;
                        mipmap.data = new Uint8Array(textureBuffer.slice(0, levelSize));
                        textureData.mipmaps.push(mipmap);
                        textureData.format = 33779;
                        textureData.type = 1009;
                    }
                } else {
                    textureData.data = new Uint8Array(textureBuffer);
                    if (textureFormat == 24) {
                        textureData.format = 1022;
                        textureData.type = 1009;
                    } else if (textureFormat == 32) {
                        textureData.format = 1023;
                        textureData.type = 1009;
                    }
                }
                this.textureDatas.push(textureData);
                this.meshDatas.push(meshData);
                this.meshNames.push(textureData.name);
            }

            var lodRanges = [];
            var rangeSize = dataView.getInt32(byteOffset, littleEndian);
            byteOffset += 4;
            for (var j = 0; j < rangeSize; j += 2) {
                var first = dataView.getFloat64(byteOffset, littleEndian);
                byteOffset += 8;
                var second = dataView.getFloat64(byteOffset, littleEndian);
                byteOffset += 8;
                var lodRange = new OSGBLodRange(first, second);
                lodRanges.push(lodRange);
            }
            this.lodRanges.push(lodRanges);

            var rangeDatas = [];
            var rangeDataCount = dataView.getInt32(byteOffset, littleEndian);
            byteOffset += 4;
            for (var j = 0; j < rangeDataCount; j++) {
                var rangeDataLength = dataView.getInt32(byteOffset, littleEndian);
                byteOffset += 4;
                var rangeData = textDecoder.decode(new Uint8Array(dataBuffer, byteOffset, rangeDataLength));
                byteOffset += rangeDataLength;
                rangeDatas.push(rangeData);
            }
            this.rangeDatas.push(rangeDatas);
        }
        return true;
    },

    toTransferableObjects: function (transferableObjects) {
        var layouts = {};
        layouts.meshNames = this.meshNames;
        layouts.lodRanges = this.lodRanges;
        layouts.rangeDatas = this.rangeDatas;
        layouts.meshDatas = [];
        for (var i = 0; i < this.meshDatas.length; i++) {
            var meshData = {};
            meshData.positionsIndex = transferableObjects.length;
            transferableObjects.push(this.meshDatas[i].positions.buffer);
            meshData.texCoordsIndex = transferableObjects.length;
            transferableObjects.push(this.meshDatas[i].texCoords.buffer);
            meshData.indexsIndex = transferableObjects.length;
            transferableObjects.push(this.meshDatas[i].indexs.buffer);
            meshData.boundingBox = this.meshDatas[i].boundingBox;
            meshData.sphereBox = this.meshDatas[i].sphereBox;
            layouts.meshDatas.push(meshData);
        }
        layouts.textureDatas = [];
        for (var i = 0; i < this.textureDatas.length; i++) {
            var textureData = {};
            textureData.name = this.textureDatas[i].name;
            textureData.format = this.textureDatas[i].format;
            textureData.type = this.textureDatas[i].type;
            textureData.width = this.textureDatas[i].width;
            textureData.height = this.textureDatas[i].height;
            textureData.compress = this.textureDatas[i].compress;
            textureData.miplevel = this.textureDatas[i].miplevel;
            if (this.textureDatas[i].data != null) {
                textureData.dataIndex = transferableObjects.length;
                transferableObjects.push(this.textureDatas[i].data.buffer);
            } else {
                textureData.dataIndex = -1;
            }
            textureData.mipmaps = [];
            var mipmaps = this.textureDatas[i].mipmaps;
            for (var j = 0; j < mipmaps.length; j++) {
                var mipmap = {};
                mipmap.width = mipmaps[j].width;
                mipmap.height = mipmaps[j].height;
                if (mipmaps[j].data != null) {
                    mipmap.dataIndex = transferableObjects.length;
                    transferableObjects.push(mipmaps[j].data.buffer);
                } else {
                    mipmap.dataIndex = -1;
                }
                textureData.mipmaps.push(mipmap);
            }
            layouts.textureDatas.push(textureData);
        }
        return layouts;
    },

    fromTransferableObjects: function (layouts, transferableObjects) {
        this.meshNames = layouts.meshNames;
        this.lodRanges = layouts.lodRanges;
        this.rangeDatas = layouts.rangeDatas;
        this.meshDatas.length = 0;
        for (var i = 0; i < layouts.meshDatas.length; i++) {
            var meshData = new OSGBMeshData();
            meshData.positions = new Float32Array(transferableObjects[layouts.meshDatas[i].positionsIndex]);
            meshData.texCoords = new Float32Array(transferableObjects[layouts.meshDatas[i].texCoordsIndex]);
            meshData.indexs = new Uint16Array(transferableObjects[layouts.meshDatas[i].indexsIndex]);
            meshData.boundingBox = layouts.meshDatas[i].boundingBox;
            meshData.sphereBox = layouts.meshDatas[i].sphereBox;
            this.meshDatas.push(meshData);
        }
        this.textureDatas.length = 0;
        for (var i = 0; i < layouts.textureDatas.length; i++) {
            var textureData = new OSGBTextureData();
            textureData.name = layouts.textureDatas[i].name;
            textureData.format = layouts.textureDatas[i].format;
            textureData.type = layouts.textureDatas[i].type;
            textureData.width = layouts.textureDatas[i].width;
            textureData.height = layouts.textureDatas[i].height;
            textureData.compress = layouts.textureDatas[i].compress;
            textureData.miplevel = layouts.textureDatas[i].miplevel;
            var dataIndex = layouts.textureDatas[i].dataIndex;
            if (dataIndex >= 0) {
                textureData.data = new Uint8Array(transferableObjects[dataIndex]);
            }
            var mipmaps = layouts.textureDatas[i].mipmaps;
            for (var j = 0; j < mipmaps.length; j++) {
                var mipmap = {};
                mipmap.width = mipmaps[j].width;
                mipmap.height = mipmaps[j].height;
                if (mipmaps[j].dataIndex >= 0) {
                    mipmap.data = new Uint8Array(transferableObjects[mipmaps[j].dataIndex]);
                }
                textureData.mipmaps.push(mipmap);
            }
            this.textureDatas.push(textureData);
        }
    },

} );
